using System;
using System.Text.RegularExpressions;
using SQLRunnerLib.Services;
using SQLRunnerLib.Util;
using SQLServices.Log;
using SQLServices.Runners;
using SQLServices.Runners.Events;

namespace SQLRunnerLib.Util
{
	/// <summary>
	/// Manages the execution of a SQL Runner project from the Command line
	/// </summary>
	public class LineController
	{
		public LineController(){}
		private long _scriptsExecuted = 0;
		private long _scriptsWithErrors = 0;
		private LineParser _args;
		private IRunner _runner;

		private bool ValidateArgs(LineParser args) 
		{
			// we need a project file
			if (args.Project == null) 
			{
				ServiceContainer.Logger.LogMessage("Missing a valid project file", MsgLevel.Error);
				LineUsage.Print();
				return false;
			}
			// The project file should contain at least one folder
			if (args.Project.Folders.Count == 0) 
			{
				ServiceContainer.Logger.LogMessage("Project file should contain at least one folder", MsgLevel.Error);
				LineUsage.Print();
				return false;
			}
			// The server name should be populated
			if (args.ServerName.Equals(string.Empty)) 
			{
				ServiceContainer.Logger.LogMessage("Server name is missing", MsgLevel.Error);
				LineUsage.Print();
				return false;
			}
			// The database name should be populated
			if (args.DatabaseName.Equals(string.Empty)) 
			{
				ServiceContainer.Logger.LogMessage("Database name is missing", MsgLevel.Error);
				LineUsage.Print();
				return false;
			}
			if (args.ShowHelp) 
			{
				LineUsage.Print();
				return false;
			}
			return true;
		}
		
		private void Connect(LineParser args) 
		{
			ServiceContainer.Logger.LogMessage("Connecting to the SQL Server", MsgLevel.Info);
			ServiceContainer.SQLConnection.OpenConnection(args.ServerName, args.UserName, args.Pwd, args.IsTrustedConnection);
			ServiceContainer.Logger.LogMessage(string.Format("Connected to SQL Server: {0}", args.ServerName), MsgLevel.Info);
			ServiceContainer.SQLConnection.GetDatabase(args.DatabaseName);
			ServiceContainer.Logger.LogMessage(string.Format("Connected to Database: {0}", args.DatabaseName), MsgLevel.Info);
		}

		public int start(LineParser args)
		{
			if (!ValidateArgs(args)) return -1;
			try {
				_args = args;
				Connect(args);
				_runner = ServiceContainer.CreateRunner(args.Project);
				HookEvents(_runner);
				_runner.RunScripts();
				UnHookEvents(_runner);
				ServiceContainer.SQLConnection.Disconnect();
				ServiceContainer.Logger.LogMessage("Disconnected from the SQL Server", MsgLevel.Info);
				return (int) _scriptsWithErrors;
			}
			catch (Exception ex) {
				Regex expression = new Regex(@"(\[(\w*\s*)*\])+");
				string msg = expression.Replace(ex.Message, "");
				ServiceContainer.Logger.LogMessage(
					String.Format("The application stopped as a result of:\n{0}", msg),
					MsgLevel.Error);
				return -1;
			}
		}

		private void HookEvents(IRunner runner)
		{
			runner.ProgressMsgCreated += new ProgressMsgCreatedEventHandler(OnProgressMsgCreated);
			runner.ScriptExecError += new ScriptExecErrorEventHandler(OnScriptExecError);
			runner.ScriptExecuted += new ScriptExecutedEventHandler(OnScriptExecuted);
			runner.ExecutionFinished += new ExecutionFinishedEventHandler(OnExecutionFinished);
		}

		private void UnHookEvents(IRunner runner)
		{
			runner.ProgressMsgCreated -= new ProgressMsgCreatedEventHandler(OnProgressMsgCreated);
			runner.ScriptExecError -= new ScriptExecErrorEventHandler(OnScriptExecError);
			runner.ScriptExecuted -= new ScriptExecutedEventHandler(OnScriptExecuted);
			runner.ExecutionFinished -= new ExecutionFinishedEventHandler(OnExecutionFinished);
		}

		private void OnProgressMsgCreated(object sender, ProgressMsgCreatedEventArgs e)
		{
			ServiceContainer.Logger.LogMessage(e.Message, MsgLevel.Info);
		}

		private void OnScriptExecError(object sender, ScriptExecutionErrorEventArgs e)
		{
			ServiceContainer.Logger.LogMessage(
				String.Format("Error executing [{0}]: {1}", e.FileName, e.ErrorMsg),
				MsgLevel.Error);
			_scriptsExecuted++;
			_scriptsWithErrors++;
			if ( ( _args.ErrorLevel & ErrorIndicator.StopExecution ) == ErrorIndicator.StopExecution ) {
				_runner.CancelExecution();
			}
		}

		private void OnScriptExecuted(object sender, ScriptExecutedEventArgs e)
		{
			ServiceContainer.Logger.LogMessage(
				String.Format("Runner - script {0} was executed", e.FileName));
			_scriptsExecuted++;
		}

		private void OnExecutionFinished(object sender, ExecutionFinishedEventArgs e)
		{
			if ( _scriptsExecuted > 0 )
				ServiceContainer.Logger.LogMessage(
					String.Format("{0} scrips was/were executed", _scriptsExecuted),
					MsgLevel.Warn);
			if ( _scriptsWithErrors > 0 )
				ServiceContainer.Logger.LogMessage(
					String.Format("{0} scrips was/were executed with errors", _scriptsWithErrors),
					MsgLevel.Warn);
			ServiceContainer.Logger.LogMessage("Application finishing", MsgLevel.Info);
		}
	}

}
